package com.gzist.springailab02.handler;

import cn.hutool.core.net.URLDecoder;
import com.gzist.springailab02.service.AIService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import javax.validation.constraints.NotNull;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * WebSocket处理器
 */
@Component
public class ChatWebSocketHandler extends TextWebSocketHandler {

    private static final Logger log = LoggerFactory.getLogger(ChatWebSocketHandler.class);

    // 线程池配置
    private static final ConcurrentHashMap<String, ChatWebSocketHandler> webSocketMap = new ConcurrentHashMap<>();
    private static final AtomicInteger onlineCount = new AtomicInteger(0);

    // 实例属性
    private WebSocketSession session;
    private String userId = "";

    // 业务服务
    private static AIService aiService;

    @Autowired
    public void setDeepSeekService(AIService aiService) {
        ChatWebSocketHandler.aiService = aiService;
    }

    /**
     * 连接建立成功时调用
     * 该方法用于处理WebSocket连接建立成功时的逻辑，包括设置会话、处理用户ID、注册连接、发送欢迎消息等操作
     *
     * @param session 当前WebSocket会话
     */
    @Override
    public void afterConnectionEstablished(@NotNull WebSocketSession session) {
        this.session = session;
        processUserId(session);
        registerConnection();
        logConnectionStatus();
    }

    /**
     * 从URI中提取用户ID
     * 该方法用于从WebSocket会话的URI中提取用户ID，并将其赋值给实例属性userId
     *
     * @param session 当前WebSocket会话
     */
    private void processUserId(WebSocketSession session) {
        String query = session.getUri().getQuery();
        Map<String, String> params = parseQueryParameters(query);
        log.info("解析参数: {}", params);
        this.userId = params.getOrDefault("userId", session.getId());
    }

    /**
     * 解析查询参数字符串为键值对映射
     * 该方法用于解析URL查询字符串，将其转换为一个键值对映射，便于后续处理
     * 查询字符串中的每个键值对用'='分隔，而键值对之间用'&'分隔
     *
     * @param query 查询字符串，包含一系列键值对
     * @return 返回一个映射，包含解析后的键值对
     */
    private Map<String, String> parseQueryParameters(String query) {
        // 创建一个HashMap来存储解析后的键值对
        Map<String, String> params = new HashMap<>();
        // 检查查询字符串是否非空
        if (query != null) {
            // 分割查询字符串为多个键值对
            for (String pair : query.split("&")) {
                // 分割键值对为键和值
                String[] kv = pair.split("=", 2);
                // 确保键值对完整
                if (kv.length == 2) {
                    try {
                        // 解码键和值，并添加到映射中
                        params.put(
                                URLDecoder.decode(kv[0], StandardCharsets.UTF_8),
                                URLDecoder.decode(kv[1], StandardCharsets.UTF_8)
                        );
                    } catch (Exception e) {
                        // 如果解码失败，记录警告日志
                        log.warn("参数解码失败: {}", pair);
                    }
                }
            }
        }
        // 返回解析后的键值对映射
        return params;
    }

    /**
     * 注册连接
     * 该方法用于将当前WebSocket连接注册到连接池中，并更新在线连接数
     * 如果已经存在同名连接，则将其关闭
     */
    private void registerConnection() {
        webSocketMap.compute(userId, (key, existingHandler) -> {
            if (existingHandler != null) {
                existingHandler.forceCloseConnection();
            }
            onlineCount.incrementAndGet();
            return this;
        });
    }

    /**
     * 强制关闭连接
     * 该方法用于强制关闭当前WebSocket连接，并更新在线连接数
     */
    public void forceCloseConnection() {
        try {
            if (session.isOpen()) {
                session.close(CloseStatus.SESSION_NOT_RELIABLE);
            }
        } catch (Exception e) {
            log.error("强制关闭连接异常: {}", e.getMessage());
        }
    }

    /**
     * 消息处理逻辑
     */
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) {
        String payload = message.getPayload();
        log.info("收到消息: userId={}, content={}", userId, payload);

        processBusinessMessage(payload);
    }

    /**
     * 处理业务消息
     * 该方法用于处理客户端发送的业务消息，并调用业务服务处理消息
     *
     * @param message 客户端发送的业务消息
     */
    private void processBusinessMessage(String message) {
        try {
            aiService.handleChatWebSocket(message, userId)
                    .subscribe(
                            response -> safeSendMessage(response),
                            error -> safeSendError(error)
                    );
        } catch (Exception e) {
            log.error("消息处理异常: {}", e.getMessage());
        }
    }

    /**
     * 安全地发送消息
     * 该方法用于安全地发送消息给客户端，如果连接已关闭，则不发送消息
     *
     * @param content 要发送的消息内容
     */
    private void safeSendMessage(String content) {
        if (session.isOpen()) {
            try {
                session.sendMessage(new TextMessage(content));
            } catch (Exception e) {
                log.error("消息发送失败: {}", e.getMessage());
            }
        }
    }

    private void safeSendError(Throwable error) {
        String errorMsg = "SYSTEM_ERROR: " + error.getMessage();
        if (session.isOpen()) {
            try {
                session.sendMessage(new TextMessage(errorMsg));
            } catch (Exception e) {
                log.error("错误消息发送失败: {}", e.getMessage());
            }
        }
    }

    // 连接关闭处理
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
        cleanupResources();
        log.info("连接关闭: userId={}, status={}", userId, status);
    }

    private void cleanupResources() {
        webSocketMap.remove(userId);
        onlineCount.updateAndGet(count -> count > 0 ? count - 1 : 0);
    }

    /**
     * 用于日志记录当前连接状态
     */
    private void logConnectionStatus() {
        log.info("当前连接状态: 在线用户={}, 总连接数={}", onlineCount.get(), webSocketMap.size());
    }

}